/*
 * Copyright 1999-2017 Alibaba Group Holding Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package studio.raptor.sqlparser.wall.spi;

import static studio.raptor.sqlparser.visitor.SQLEvalVisitor.EVAL_VALUE;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
import java.util.Set;
import java.util.Stack;
import studio.raptor.sqlparser.SQLUtils;
import studio.raptor.sqlparser.ast.SQLCommentHint;
import studio.raptor.sqlparser.ast.SQLExpr;
import studio.raptor.sqlparser.ast.SQLLimit;
import studio.raptor.sqlparser.ast.SQLName;
import studio.raptor.sqlparser.ast.SQLObject;
import studio.raptor.sqlparser.ast.SQLOrderBy;
import studio.raptor.sqlparser.ast.SQLStatement;
import studio.raptor.sqlparser.ast.expr.SQLAggregateExpr;
import studio.raptor.sqlparser.ast.expr.SQLAllColumnExpr;
import studio.raptor.sqlparser.ast.expr.SQLBetweenExpr;
import studio.raptor.sqlparser.ast.expr.SQLBinaryOpExpr;
import studio.raptor.sqlparser.ast.expr.SQLBinaryOperator;
import studio.raptor.sqlparser.ast.expr.SQLBooleanExpr;
import studio.raptor.sqlparser.ast.expr.SQLCaseExpr;
import studio.raptor.sqlparser.ast.expr.SQLCaseExpr.Item;
import studio.raptor.sqlparser.ast.expr.SQLCharExpr;
import studio.raptor.sqlparser.ast.expr.SQLExistsExpr;
import studio.raptor.sqlparser.ast.expr.SQLIdentifierExpr;
import studio.raptor.sqlparser.ast.expr.SQLInListExpr;
import studio.raptor.sqlparser.ast.expr.SQLInSubQueryExpr;
import studio.raptor.sqlparser.ast.expr.SQLIntegerExpr;
import studio.raptor.sqlparser.ast.expr.SQLLiteralExpr;
import studio.raptor.sqlparser.ast.expr.SQLMethodInvokeExpr;
import studio.raptor.sqlparser.ast.expr.SQLNCharExpr;
import studio.raptor.sqlparser.ast.expr.SQLNotExpr;
import studio.raptor.sqlparser.ast.expr.SQLNumberExpr;
import studio.raptor.sqlparser.ast.expr.SQLNumericLiteralExpr;
import studio.raptor.sqlparser.ast.expr.SQLPropertyExpr;
import studio.raptor.sqlparser.ast.expr.SQLQueryExpr;
import studio.raptor.sqlparser.ast.expr.SQLUnaryExpr;
import studio.raptor.sqlparser.ast.expr.SQLValuableExpr;
import studio.raptor.sqlparser.ast.expr.SQLVariantRefExpr;
import studio.raptor.sqlparser.ast.statement.SQLAlterTableStatement;
import studio.raptor.sqlparser.ast.statement.SQLBlockStatement;
import studio.raptor.sqlparser.ast.statement.SQLCallStatement;
import studio.raptor.sqlparser.ast.statement.SQLCreateIndexStatement;
import studio.raptor.sqlparser.ast.statement.SQLCreateSequenceStatement;
import studio.raptor.sqlparser.ast.statement.SQLCreateTableStatement;
import studio.raptor.sqlparser.ast.statement.SQLCreateTriggerStatement;
import studio.raptor.sqlparser.ast.statement.SQLCreateViewStatement;
import studio.raptor.sqlparser.ast.statement.SQLDeleteStatement;
import studio.raptor.sqlparser.ast.statement.SQLDescribeStatement;
import studio.raptor.sqlparser.ast.statement.SQLDropIndexStatement;
import studio.raptor.sqlparser.ast.statement.SQLDropProcedureStatement;
import studio.raptor.sqlparser.ast.statement.SQLDropSequenceStatement;
import studio.raptor.sqlparser.ast.statement.SQLDropTableStatement;
import studio.raptor.sqlparser.ast.statement.SQLDropTriggerStatement;
import studio.raptor.sqlparser.ast.statement.SQLDropViewStatement;
import studio.raptor.sqlparser.ast.statement.SQLExprTableSource;
import studio.raptor.sqlparser.ast.statement.SQLInsertInto;
import studio.raptor.sqlparser.ast.statement.SQLInsertStatement;
import studio.raptor.sqlparser.ast.statement.SQLInsertStatement.ValuesClause;
import studio.raptor.sqlparser.ast.statement.SQLJoinTableSource;
import studio.raptor.sqlparser.ast.statement.SQLMergeStatement;
import studio.raptor.sqlparser.ast.statement.SQLRollbackStatement;
import studio.raptor.sqlparser.ast.statement.SQLSelect;
import studio.raptor.sqlparser.ast.statement.SQLSelectGroupByClause;
import studio.raptor.sqlparser.ast.statement.SQLSelectItem;
import studio.raptor.sqlparser.ast.statement.SQLSelectQuery;
import studio.raptor.sqlparser.ast.statement.SQLSelectQueryBlock;
import studio.raptor.sqlparser.ast.statement.SQLSelectStatement;
import studio.raptor.sqlparser.ast.statement.SQLSetStatement;
import studio.raptor.sqlparser.ast.statement.SQLShowTablesStatement;
import studio.raptor.sqlparser.ast.statement.SQLStartTransactionStatement;
import studio.raptor.sqlparser.ast.statement.SQLSubqueryTableSource;
import studio.raptor.sqlparser.ast.statement.SQLTableSource;
import studio.raptor.sqlparser.ast.statement.SQLTruncateStatement;
import studio.raptor.sqlparser.ast.statement.SQLUnionOperator;
import studio.raptor.sqlparser.ast.statement.SQLUnionQuery;
import studio.raptor.sqlparser.ast.statement.SQLUpdateSetItem;
import studio.raptor.sqlparser.ast.statement.SQLUpdateStatement;
import studio.raptor.sqlparser.ast.statement.SQLUseStatement;
import studio.raptor.sqlparser.dialect.mysql.ast.expr.MySqlOrderingExpr;
import studio.raptor.sqlparser.dialect.mysql.ast.expr.MySqlOutFileExpr;
import studio.raptor.sqlparser.dialect.mysql.ast.statement.MySqlCommitStatement;
import studio.raptor.sqlparser.dialect.mysql.ast.statement.MySqlDeleteStatement;
import studio.raptor.sqlparser.dialect.mysql.ast.statement.MySqlExplainStatement;
import studio.raptor.sqlparser.dialect.mysql.ast.statement.MySqlHintStatement;
import studio.raptor.sqlparser.dialect.mysql.ast.statement.MySqlInsertStatement;
import studio.raptor.sqlparser.dialect.mysql.ast.statement.MySqlLockTableStatement;
import studio.raptor.sqlparser.dialect.mysql.ast.statement.MySqlRenameTableStatement;
import studio.raptor.sqlparser.dialect.mysql.ast.statement.MySqlReplaceStatement;
import studio.raptor.sqlparser.dialect.mysql.ast.statement.MySqlSetCharSetStatement;
import studio.raptor.sqlparser.dialect.mysql.ast.statement.MySqlSetNamesStatement;
import studio.raptor.sqlparser.dialect.mysql.ast.statement.MySqlShowGrantsStatement;
import studio.raptor.sqlparser.dialect.mysql.ast.statement.MySqlShowStatement;
import studio.raptor.sqlparser.dialect.mysql.ast.statement.MySqlUpdateStatement;
import studio.raptor.sqlparser.dialect.mysql.parser.MySqlStatementParser;
import studio.raptor.sqlparser.dialect.oracle.ast.stmt.OracleMultiInsertStatement;
import studio.raptor.sqlparser.dialect.postgresql.ast.stmt.PGShowStatement;
import studio.raptor.sqlparser.dialect.sqlserver.ast.stmt.SQLServerCommitStatement;
import studio.raptor.sqlparser.dialect.sqlserver.ast.stmt.SQLServerExecStatement;
import studio.raptor.sqlparser.dialect.sqlserver.ast.stmt.SQLServerInsertStatement;
import studio.raptor.sqlparser.dialect.sqlserver.ast.stmt.SQLServerSetStatement;
import studio.raptor.sqlparser.parser.SQLStatementParser;
import studio.raptor.sqlparser.util.JdbcUtils;
import studio.raptor.sqlparser.util.Log;
import studio.raptor.sqlparser.util.StringUtils;
import studio.raptor.sqlparser.visitor.ExportParameterVisitor;
import studio.raptor.sqlparser.visitor.SQLEvalVisitor;
import studio.raptor.sqlparser.visitor.SQLEvalVisitorUtils;
import studio.raptor.sqlparser.visitor.functions.Nil;
import studio.raptor.sqlparser.wall.WallConfig;
import studio.raptor.sqlparser.wall.WallConfig.TenantCallBack;
import studio.raptor.sqlparser.wall.WallConfig.TenantCallBack.StatementType;
import studio.raptor.sqlparser.wall.WallContext;
import studio.raptor.sqlparser.wall.WallProvider;
import studio.raptor.sqlparser.wall.WallSqlTableStat;
import studio.raptor.sqlparser.wall.WallVisitor;
import studio.raptor.sqlparser.wall.violation.ErrorCode;
import studio.raptor.sqlparser.wall.violation.IllegalSQLObjectViolation;

public class WallVisitorUtils {

  public final static String HAS_TRUE_LIKE = "hasTrueLike";
  public final static String[] whiteHints = {"LOCAL", "TEMPORARY", "SQL_NO_CACHE", "SQL_CACHE",
      "HIGH_PRIORITY",
      "LOW_PRIORITY", "STRAIGHT_JOIN", "SQL_BUFFER_RESULT", "SQL_BIG_RESULT", "SQL_SMALL_RESULT",
      "DELAYED"};
  private final static Log LOG = new Log();
  private static ThreadLocal<WallConditionContext> wallConditionContextLocal = new ThreadLocal<WallConditionContext>();
  private static ThreadLocal<WallTopStatementContext> wallTopStatementContextLocal = new ThreadLocal<WallTopStatementContext>();

  public static void check(WallVisitor visitor, SQLInListExpr x) {

  }

  public static boolean check(WallVisitor visitor, SQLBinaryOpExpr x) {

    if (x.getOperator() == SQLBinaryOperator.BooleanOr
        || x.getOperator() == SQLBinaryOperator.BooleanAnd) {
      List<SQLExpr> groupList = SQLUtils.split(x);
      for (SQLExpr item : groupList) {
        item.accept(visitor);
      }
      return false;
    }

    if (x.getOperator() == SQLBinaryOperator.Add || x.getOperator() == SQLBinaryOperator.Concat) {
      List<SQLExpr> groupList = SQLUtils.split(x);
      if (groupList.size() >= 4) {
        int chrCount = 0;
        for (int i = 0; i < groupList.size(); ++i) {
          SQLExpr item = groupList.get(i);
          if (item instanceof SQLMethodInvokeExpr) {
            SQLMethodInvokeExpr methodExpr = (SQLMethodInvokeExpr) item;
            String methodName = methodExpr.getMethodName().toLowerCase();
            if ("chr".equals(methodName) || "char".equals(methodName)) {
              if (methodExpr.getParameters().get(0) instanceof SQLLiteralExpr) {
                chrCount++;
              }
            }
          } else if (item instanceof SQLCharExpr) {
            if (((SQLCharExpr) item).getText().length() > 5) {
              chrCount = 0;
              continue;
            }
          }

          if (chrCount >= 4) {
            addViolation(visitor, ErrorCode.EVIL_CONCAT, "evil concat", x);
            break;
          }
        }
      }
    }

    return true;
  }

  public static void check(WallVisitor visitor, SQLCreateTableStatement x) {
    String tableName = ((SQLName) x.getName()).getSimpleName();
    WallContext context = WallContext.current();
    if (context != null) {
      WallSqlTableStat tableStat = context.getTableStat(tableName);
      if (tableStat != null) {
        tableStat.incrementCreateCount();
      }
    }
  }

  public static void check(WallVisitor visitor, SQLAlterTableStatement x) {
    String tableName = ((SQLName) x.getName()).getSimpleName();
    WallContext context = WallContext.current();
    if (context != null) {
      WallSqlTableStat tableStat = context.getTableStat(tableName);
      if (tableStat != null) {
        tableStat.incrementAlterCount();
      }
    }
  }

  public static void check(WallVisitor visitor, SQLDropTableStatement x) {
    for (SQLTableSource item : x.getTableSources()) {
      if (item instanceof SQLExprTableSource) {
        SQLExpr expr = ((SQLExprTableSource) item).getExpr();
        String tableName = ((SQLName) expr).getSimpleName();
        WallContext context = WallContext.current();
        if (context != null) {
          WallSqlTableStat tableStat = context.getTableStat(tableName);
          if (tableStat != null) {
            tableStat.incrementDropCount();
          }
        }
      }
    }
  }

  public static void check(WallVisitor visitor, SQLSelectItem x) {
    SQLExpr expr = x.getExpr();

    if (expr instanceof SQLVariantRefExpr) {
      if (!isTopSelectItem(expr) && "@".equals(((SQLVariantRefExpr) expr).getName())) {
        addViolation(visitor, ErrorCode.EVIL_NAME, "@ not allow", x);
      }
    }

    if (visitor.getConfig().isSelectAllColumnAllow()) {
      return;
    }

    if (expr instanceof SQLAllColumnExpr //
        && x.getParent() instanceof SQLSelectQueryBlock) {
      SQLSelectQueryBlock queryBlock = (SQLSelectQueryBlock) x.getParent();
      SQLTableSource from = queryBlock.getFrom();

      if (from instanceof SQLExprTableSource) {
        addViolation(visitor, ErrorCode.SELECT_NOT_ALLOW, "'SELECT *' not allow", x);
      }
    }
  }

  public static void check(WallVisitor visitor, SQLPropertyExpr x) {
    checkSchema(visitor, x.getOwner());
  }

  public static void checkInsert(WallVisitor visitor, SQLInsertInto x) {
    checkReadOnly(visitor, x.getTableSource());

    if (!visitor.getConfig().isInsertAllow()) {
      addViolation(visitor, ErrorCode.INSERT_NOT_ALLOW, "insert not allow", x);
    }

    checkInsertForMultiTenant(visitor, x);
  }

  public static void checkSelelct(WallVisitor visitor, SQLSelectQueryBlock x) {

    for (SQLSelectItem item : x.getSelectList()) {
      item.setParent(x);
    }

    if (x.getInto() != null) {
      checkReadOnly(visitor, x.getInto());
    }

    if (!visitor.getConfig().isSelectIntoAllow() && x.getInto() != null) {
      addViolation(visitor, ErrorCode.SELECT_INTO_NOT_ALLOW, "select into not allow", x);
      return;
    }

    if (x.getFrom() != null) {
      x.getFrom().setParent(x);
    }

    SQLExpr where = x.getWhere();
    if (where != null) {
      where.setParent(x);
      checkCondition(visitor, x.getWhere());

      Object whereValue = getConditionValue(visitor, where,
          visitor.getConfig().isSelectWhereAlwayTrueCheck());

      if (Boolean.TRUE == whereValue) {
        if (visitor.getConfig().isSelectWhereAlwayTrueCheck() && visitor.isSqlEndOfComment()
            && !isSimpleConstExpr(where)) {// 简单表达式
          addViolation(visitor, ErrorCode.ALWAYS_TRUE, "select alway true condition not allow", x);
        }
      }
    }
    checkSelectForMultiTenant(visitor, x);
    // checkConditionForMultiTenant(visitor, x.getWhere(), x);
  }

  public static void checkHaving(WallVisitor visitor, SQLExpr x) {
    if (x == null) {
      return;
    }

    if (Boolean.TRUE == getConditionValue(visitor, x,
        visitor.getConfig().isSelectHavingAlwayTrueCheck())) {
      if (visitor.getConfig().isSelectHavingAlwayTrueCheck() && visitor.isSqlEndOfComment()
          && !isSimpleConstExpr(x)) {
        addViolation(visitor, ErrorCode.ALWAYS_TRUE, "having alway true condition not allow", x);
      }
    }
  }

  public static void checkDelete(WallVisitor visitor, SQLDeleteStatement x) {
    checkReadOnly(visitor, x.getTableSource());

    WallConfig config = visitor.getConfig();
    if (!config.isDeleteAllow()) {
      addViolation(visitor, ErrorCode.INSERT_NOT_ALLOW, "delete not allow", x);
      return;
    }

    boolean hasUsing = false;

    if (x instanceof MySqlDeleteStatement) {
      hasUsing = ((MySqlDeleteStatement) x).getUsing() != null;
    }

    boolean isJoinTableSource = x.getTableSource() instanceof SQLJoinTableSource;
    if (x.getWhere() == null && (!hasUsing) && !isJoinTableSource) {
      WallContext context = WallContext.current();
      if (context != null) {
        context.incrementDeleteNoneConditionWarnings();
      }

      if (config.isDeleteWhereNoneCheck()) {
        addViolation(visitor, ErrorCode.NONE_CONDITION, "delete none condition not allow", x);
        return;
      }
    }

    SQLExpr where = x.getWhere();
    if (where != null) {
      checkCondition(visitor, where);

      if (Boolean.TRUE == getConditionValue(visitor, where, config.isDeleteWhereAlwayTrueCheck())) {
        if (config.isDeleteWhereAlwayTrueCheck() && visitor.isSqlEndOfComment()
            && !isSimpleConstExpr(where)) {
          addViolation(visitor, ErrorCode.ALWAYS_TRUE, "delete alway true condition not allow", x);
        }
      }
    }

    // checkConditionForMultiTenant(visitor, x.getWhere(), x);
  }

  private static boolean isSimpleConstExpr(SQLExpr sqlExpr) {
    List<SQLExpr> parts = getParts(sqlExpr);
    if (parts.isEmpty()) {
      return false;
    }

    for (SQLExpr part : parts) {
      if (isFirst(part)) {
        Object evalValue = part.getAttribute(EVAL_VALUE);
        if (evalValue == null) {
          if (part instanceof SQLBooleanExpr) {
            evalValue = ((SQLBooleanExpr) part).getValue();
          } else if (part instanceof SQLNumericLiteralExpr) {
            evalValue = ((SQLNumericLiteralExpr) part).getNumber();
          } else if (part instanceof SQLCharExpr) {
            evalValue = ((SQLCharExpr) part).getText();
          } else if (part instanceof SQLNCharExpr) {
            evalValue = ((SQLNCharExpr) part).getText();
          }
        }
        Boolean result = SQLEvalVisitorUtils.castToBoolean(evalValue);
        if (result != null && result) {
          return true;
        }
      }
      boolean isSimpleConstExpr = false;
      if (part == sqlExpr || part instanceof SQLLiteralExpr) {
        isSimpleConstExpr = true;
      } else if (part instanceof SQLBinaryOpExpr) {
        SQLBinaryOpExpr binaryOpExpr = (SQLBinaryOpExpr) part;

        if (binaryOpExpr.getOperator() == SQLBinaryOperator.Equality
            || binaryOpExpr.getOperator() == SQLBinaryOperator.NotEqual
            || binaryOpExpr.getOperator() == SQLBinaryOperator.GreaterThan) {
          if (binaryOpExpr.getLeft() instanceof SQLIntegerExpr
              && binaryOpExpr.getRight() instanceof SQLIntegerExpr) {
            isSimpleConstExpr = true;
          }
        }
      }

      if (!isSimpleConstExpr) {
        return false;
      }
    }
    return true;
  }

  private static void checkCondition(WallVisitor visitor, SQLExpr x) {
    if (x == null) {
      return;
    }

    if (visitor.getConfig().isMustParameterized()) {
      ExportParameterVisitor exportParameterVisitor = visitor.getProvider()
          .createExportParameterVisitor();
      x.accept(exportParameterVisitor);

      if (exportParameterVisitor.getParameters().size() > 0) {
        addViolation(visitor, ErrorCode.NOT_PARAMETERIZED, "sql must parameterized", x);
      }
    }

  }

  private static void checkJoinSelectForMultiTenant(WallVisitor visitor, SQLJoinTableSource join,
      SQLSelectQueryBlock x) {
    TenantCallBack tenantCallBack = visitor.getConfig().getTenantCallBack();
    String tenantTablePattern = visitor.getConfig().getTenantTablePattern();
    if (tenantCallBack == null && (tenantTablePattern == null
        || tenantTablePattern.length() == 0)) {
      return;
    }

    SQLTableSource right = join.getRight();
    if (right instanceof SQLExprTableSource) {
      SQLExpr tableExpr = ((SQLExprTableSource) right).getExpr();

      if (tableExpr instanceof SQLIdentifierExpr) {
        String tableName = ((SQLIdentifierExpr) tableExpr).getName();

        String alias = null;
        String tenantColumn = null;
        if (tenantCallBack != null) {
          tenantColumn = tenantCallBack.getTenantColumn(StatementType.SELECT, tableName);
        }

//                if (StringUtils.isEmpty(tenantColumn)
//                    && ServletPathMatcher.getInstance().matches(tenantTablePattern, tableName)) {
//                    tenantColumn = visitor.getConfig().getTenantColumn();
//                }

        if (!StringUtils.isEmpty(tenantColumn)) {
          alias = right.getAlias();
          if (alias == null) {
            alias = tableName;
          }

          SQLExpr item = null;
          if (alias != null) {
            item = new SQLPropertyExpr(new SQLIdentifierExpr(alias), tenantColumn);
          } else {
            item = new SQLIdentifierExpr(tenantColumn);
          }
          SQLSelectItem selectItem = new SQLSelectItem(item);
          x.getSelectList().add(selectItem);
          visitor.setSqlModified(true);
        }
      }
    }
  }

  private static boolean isSelectStatmentForMultiTenant(SQLSelectQueryBlock queryBlock) {

    SQLObject parent = queryBlock.getParent();
    while (parent != null) {

      if (parent instanceof SQLUnionQuery) {
        SQLObject x = parent;
        parent = x.getParent();
      } else {
        break;
      }
    }

    if (!(parent instanceof SQLSelect)) {
      return false;
    }

    parent = ((SQLSelect) parent).getParent();
    if (parent instanceof SQLSelectStatement) {
      return true;
    }

    return false;
  }

  private static void checkSelectForMultiTenant(WallVisitor visitor, SQLSelectQueryBlock x) {
    TenantCallBack tenantCallBack = visitor.getConfig().getTenantCallBack();
    String tenantTablePattern = visitor.getConfig().getTenantTablePattern();
    if (tenantCallBack == null && (tenantTablePattern == null
        || tenantTablePattern.length() == 0)) {
      return;
    }

    if (x == null) {
      throw new IllegalStateException("x is null");
    }

    if (!isSelectStatmentForMultiTenant(x)) {
      return;
    }

    SQLTableSource tableSource = x.getFrom();
    String alias = null;
    String matchTableName = null;
    String tenantColumn = null;
    if (tableSource instanceof SQLExprTableSource) {
      SQLExpr tableExpr = ((SQLExprTableSource) tableSource).getExpr();

      if (tableExpr instanceof SQLIdentifierExpr) {
        String tableName = ((SQLIdentifierExpr) tableExpr).getName();

        if (tenantCallBack != null) {
          tenantColumn = tenantCallBack.getTenantColumn(StatementType.SELECT, tableName);
        }

//                if (StringUtils.isEmpty(tenantColumn)
//                    && ServletPathMatcher.getInstance().matches(tenantTablePattern, tableName)) {
//                    tenantColumn = visitor.getConfig().getTenantColumn();
//                }

        if (!StringUtils.isEmpty(tenantColumn)) {
          matchTableName = tableName;
          alias = tableSource.getAlias();
        }
      }
    } else if (tableSource instanceof SQLJoinTableSource) {
      SQLJoinTableSource join = (SQLJoinTableSource) tableSource;
      if (join.getLeft() instanceof SQLExprTableSource) {
        SQLExpr tableExpr = ((SQLExprTableSource) join.getLeft()).getExpr();

        if (tableExpr instanceof SQLIdentifierExpr) {
          String tableName = ((SQLIdentifierExpr) tableExpr).getName();

          if (tenantCallBack != null) {
            tenantColumn = tenantCallBack.getTenantColumn(StatementType.SELECT, tableName);
          }

//                    if (StringUtils.isEmpty(tenantColumn)
//                        && ServletPathMatcher.getInstance().matches(tenantTablePattern, tableName)) {
//                        tenantColumn = visitor.getConfig().getTenantColumn();
//                    }

          if (!StringUtils.isEmpty(tenantColumn)) {
            matchTableName = tableName;
            alias = join.getLeft().getAlias();

            if (alias == null) {
              alias = tableName;
            }
          }
        }
        checkJoinSelectForMultiTenant(visitor, join, x);
      } else {
        checkJoinSelectForMultiTenant(visitor, join, x);
      }
    }

    if (matchTableName == null) {
      return;
    }

    SQLExpr item = null;
    if (alias != null) {
      item = new SQLPropertyExpr(new SQLIdentifierExpr(alias), tenantColumn);
    } else {
      item = new SQLIdentifierExpr(tenantColumn);
    }
    SQLSelectItem selectItem = new SQLSelectItem(item);
    x.getSelectList().add(selectItem);
    visitor.setSqlModified(true);
  }

  private static void checkUpdateForMultiTenant(WallVisitor visitor, SQLUpdateStatement x) {
    TenantCallBack tenantCallBack = visitor.getConfig().getTenantCallBack();
    String tenantTablePattern = visitor.getConfig().getTenantTablePattern();
    if (tenantCallBack == null && (tenantTablePattern == null
        || tenantTablePattern.length() == 0)) {
      return;
    }

    if (x == null) {
      throw new IllegalStateException("x is null");
    }

    SQLTableSource tableSource = x.getTableSource();
    String alias = null;
    String matchTableName = null;
    String tenantColumn = null;
    if (tableSource instanceof SQLExprTableSource) {
      SQLExpr tableExpr = ((SQLExprTableSource) tableSource).getExpr();
      if (tableExpr instanceof SQLIdentifierExpr) {
        String tableName = ((SQLIdentifierExpr) tableExpr).getName();

        if (tenantCallBack != null) {
          tenantColumn = tenantCallBack.getTenantColumn(StatementType.UPDATE, tableName);
        }
//                if (StringUtils.isEmpty(tenantColumn)
//                    && ServletPathMatcher.getInstance().matches(tenantTablePattern, tableName)) {
//                    tenantColumn = visitor.getConfig().getTenantColumn();
//                }

        if (!StringUtils.isEmpty(tenantColumn)) {
          matchTableName = tableName;
          alias = tableSource.getAlias();
        }
      }
    }

    if (matchTableName == null) {
      return;
    }

    SQLExpr item = null;
    if (alias != null) {
      item = new SQLPropertyExpr(new SQLIdentifierExpr(alias), tenantColumn);
    } else {
      item = new SQLIdentifierExpr(tenantColumn);
    }
    SQLExpr value = generateTenantValue(visitor, alias, StatementType.UPDATE, matchTableName);

    SQLUpdateSetItem updateSetItem = new SQLUpdateSetItem();
    updateSetItem.setColumn(item);
    updateSetItem.setValue(value);

    x.addItem(updateSetItem);
    visitor.setSqlModified(true);
  }

  private static void checkInsertForMultiTenant(WallVisitor visitor, SQLInsertInto x) {
    TenantCallBack tenantCallBack = visitor.getConfig().getTenantCallBack();
    String tenantTablePattern = visitor.getConfig().getTenantTablePattern();
    if (tenantCallBack == null && (tenantTablePattern == null
        || tenantTablePattern.length() == 0)) {
      return;
    }

    if (x == null) {
      throw new IllegalStateException("x is null");
    }

    SQLExprTableSource tableSource = x.getTableSource();
    String alias = null;
    String matchTableName = null;
    String tenantColumn = null;
    SQLExpr tableExpr = tableSource.getExpr();
    if (tableExpr instanceof SQLIdentifierExpr) {
      String tableName = ((SQLIdentifierExpr) tableExpr).getName();

      if (tenantCallBack != null) {
        tenantColumn = tenantCallBack.getTenantColumn(StatementType.INSERT, tableName);
      }
//            if (StringUtils.isEmpty(tenantColumn)
//                && ServletPathMatcher.getInstance().matches(tenantTablePattern, tableName)) {
//                tenantColumn = visitor.getConfig().getTenantColumn();
//            }

      if (!StringUtils.isEmpty(tenantColumn)) {
        matchTableName = tableName;
        alias = tableSource.getAlias();
      }
    }

    if (matchTableName == null) {
      return;
    }

    SQLExpr item = null;
    if (alias != null) {
      item = new SQLPropertyExpr(new SQLIdentifierExpr(alias), tenantColumn);
    } else {
      item = new SQLIdentifierExpr(tenantColumn);
    }
    SQLExpr value = generateTenantValue(visitor, alias, StatementType.INSERT, matchTableName);

    // add insert item and value
    x.getColumns().add(item);

    List<ValuesClause> valuesClauses = null;
    ValuesClause valuesClause = null;
    if (x instanceof MySqlInsertStatement) {
      valuesClauses = ((MySqlInsertStatement) x).getValuesList();
    } else if (x instanceof SQLServerInsertStatement) {
      valuesClauses = ((MySqlInsertStatement) x).getValuesList();
    } else {
      valuesClause = x.getValues();
    }

    if (valuesClauses != null && valuesClauses.size() > 0) {
      for (ValuesClause clause : valuesClauses) {
        clause.addValue(value);
      }
    }
    if (valuesClause != null) {
      valuesClause.addValue(value);
    }

    // insert .. select
    SQLSelect select = x.getQuery();
    if (select != null) {
      List<SQLSelectQueryBlock> queryBlocks = splitSQLSelectQuery(select.getQuery());
      for (SQLSelectQueryBlock queryBlock : queryBlocks) {
        queryBlock.getSelectList().add(new SQLSelectItem(value));
      }
    }

    visitor.setSqlModified(true);
  }

  private static List<SQLSelectQueryBlock> splitSQLSelectQuery(SQLSelectQuery x) {
    List<SQLSelectQueryBlock> groupList = new ArrayList<SQLSelectQueryBlock>();
    Stack<SQLSelectQuery> stack = new Stack<SQLSelectQuery>();

    stack.push(x);
    do {
      SQLSelectQuery query = stack.pop();
      if (query instanceof SQLSelectQueryBlock) {
        groupList.add((SQLSelectQueryBlock) query);
      } else if (query instanceof SQLUnionQuery) {
        SQLUnionQuery unionQuery = (SQLUnionQuery) query;
        stack.push(unionQuery.getLeft());
        stack.push(unionQuery.getRight());
      }
    } while (!stack.empty());
    return groupList;
  }

  private static SQLExpr generateTenantValue(WallVisitor visitor, String alias,
      StatementType statementType,
      String tableName) {
    SQLExpr value;
    TenantCallBack callBack = visitor.getConfig().getTenantCallBack();
    if (callBack != null) {
      WallProvider.setTenantValue(callBack.getTenantValue(statementType, tableName));
    }

    Object tenantValue = WallProvider.getTenantValue();
    if (tenantValue instanceof Number) {
      value = new SQLNumberExpr((Number) tenantValue);
    } else if (tenantValue instanceof String) {
      value = new SQLCharExpr((String) tenantValue);
    } else {
      throw new IllegalStateException("tenant value not support type " + tenantValue);
    }

    return value;
  }

  public static void checkReadOnly(WallVisitor visitor, SQLTableSource tableSource) {
    if (tableSource instanceof SQLExprTableSource) {
      String tableName = null;
      SQLExpr tableNameExpr = ((SQLExprTableSource) tableSource).getExpr();
      if (tableNameExpr instanceof SQLName) {
        tableName = ((SQLName) tableNameExpr).getSimpleName();
      }

      boolean readOnlyValid = visitor.getProvider().checkReadOnlyTable(tableName);
      if (!readOnlyValid) {
        addViolation(visitor, ErrorCode.READ_ONLY, "table readonly : " + tableName, tableSource);
      }
    } else if (tableSource instanceof SQLJoinTableSource) {
      SQLJoinTableSource join = (SQLJoinTableSource) tableSource;

      checkReadOnly(visitor, join.getLeft());
      checkReadOnly(visitor, join.getRight());
    }
  }

  public static void checkUpdate(WallVisitor visitor, SQLUpdateStatement x) {
    checkReadOnly(visitor, x.getTableSource());

    WallConfig config = visitor.getConfig();
    if (!config.isUpdateAllow()) {
      addViolation(visitor, ErrorCode.UPDATE_NOT_ALLOW, "update not allow", x);
      return;
    }

    SQLExpr where = x.getWhere();
    if (where == null) {
      WallContext context = WallContext.current();
      if (context != null) {
        context.incrementUpdateNoneConditionWarnings();
      }

      if (config.isUpdateWhereNoneCheck()) {
        if (x instanceof MySqlUpdateStatement) {
          MySqlUpdateStatement mysqlUpdate = (MySqlUpdateStatement) x;
          if (mysqlUpdate.getLimit() == null) {
            addViolation(visitor, ErrorCode.NONE_CONDITION, "update none condition not allow", x);
            return;
          }
        } else {
          addViolation(visitor, ErrorCode.NONE_CONDITION, "update none condition not allow", x);
          return;
        }
      }
    } else {
      where.setParent(x);
      checkCondition(visitor, where);

      if (Boolean.TRUE == getConditionValue(visitor, where, config.isUpdateWhereAlayTrueCheck())) {
        if (config.isUpdateWhereAlayTrueCheck() && visitor.isSqlEndOfComment()
            && !isSimpleConstExpr(where)) {
          addViolation(visitor, ErrorCode.ALWAYS_TRUE, "update alway true condition not allow", x);
        }
      }
    }

    checkUpdateForMultiTenant(visitor, x);
  }

  public static Object getValue(WallVisitor visitor, SQLBinaryOpExpr x) {
    if (x.getOperator() == SQLBinaryOperator.BooleanOr) {
      List<SQLExpr> groupList = SQLUtils.split(x);

      boolean allFalse = true;
      for (int i = groupList.size() - 1; i >= 0; --i) {
        SQLExpr item = groupList.get(i);
        Object result = getValue(visitor, item);
        Boolean booleanVal = SQLEvalVisitorUtils.castToBoolean(result);
        if (Boolean.TRUE == booleanVal) {
          final WallConditionContext wallContext = WallVisitorUtils.getWallConditionContext();
          if (wallContext != null && !isFirst(item)) {
            wallContext.setPartAlwayTrue(true);
          }
          return true;
        }

        if (Boolean.FALSE != booleanVal) {
          allFalse = false;
        }
      }

      if (allFalse) {
        return false;
      }

      return null;
    }

    if (x.getOperator() == SQLBinaryOperator.BooleanAnd) {

      List<SQLExpr> groupList = SQLUtils.split(x);

      int dalConst = 0;
      Boolean allTrue = Boolean.TRUE;
      for (int i = groupList.size() - 1; i >= 0; --i) {

        SQLExpr item = groupList.get(i);
        Object result = getValue(visitor, item);
        Boolean booleanVal = SQLEvalVisitorUtils.castToBoolean(result);

        if (Boolean.TRUE == booleanVal) {
          final WallConditionContext wallContext = WallVisitorUtils.getWallConditionContext();
          if (wallContext != null && !isFirst(item)) {
            wallContext.setPartAlwayTrue(true);
          }
          dalConst++;
        } else if (Boolean.FALSE == booleanVal) {
          final WallConditionContext wallContext = WallVisitorUtils.getWallConditionContext();
          if (wallContext != null && !isFirst(item)) {
            wallContext.setPartAlwayFalse(true);
          }
          allTrue = Boolean.FALSE;
          dalConst++;
        } else {
          if (allTrue != Boolean.FALSE) {
            allTrue = null;
          }
          dalConst = 0;
        }

        if (dalConst == 2 && visitor != null && !visitor.getConfig()
            .isConditionDoubleConstAllow()) {
          addViolation(visitor, ErrorCode.DOUBLE_CONST_CONDITION, "double const condition", x);
        }
      }

      if (Boolean.TRUE == allTrue) {
        return true;
      } else if (Boolean.FALSE == allTrue) {
        return false;
      }
      return null;
    }

    boolean checkCondition = visitor != null
        && (!visitor.getConfig().isConstArithmeticAllow()
        || !visitor.getConfig().isConditionOpBitwseAllow() || !visitor.getConfig()
        .isConditionOpXorAllow());

    if (x.getLeft() instanceof SQLName) {
      if (x.getRight() instanceof SQLName) {
        if (x.getLeft().toString().equalsIgnoreCase(x.getRight().toString())) {
          switch (x.getOperator()) {
            case Equality:
            case Like:
              return Boolean.TRUE;
            case NotEqual:
            case GreaterThan:
            case GreaterThanOrEqual:
            case LessThan:
            case LessThanOrEqual:
            case LessThanOrGreater:
            case NotLike:
              return Boolean.FALSE;
            default:
              break;
          }
        }
      } else if (!checkCondition) {
        switch (x.getOperator()) {
          case Equality:
          case NotEqual:
          case GreaterThan:
          case GreaterThanOrEqual:
          case LessThan:
          case LessThanOrEqual:
          case LessThanOrGreater:
            return null;
          default:
            break;
        }
      }
    }

    if (x.getLeft() instanceof SQLValuableExpr && x.getRight() instanceof SQLValuableExpr) {
      Object leftValue = ((SQLValuableExpr) x.getLeft()).getValue();
      Object rightValue = ((SQLValuableExpr) x.getRight()).getValue();
      if (x.getOperator() == SQLBinaryOperator.Equality) {
        boolean evalValue = SQLEvalVisitorUtils.eq(leftValue, rightValue);
        x.putAttribute(EVAL_VALUE, evalValue);
        return evalValue;
      } else if (x.getOperator() == SQLBinaryOperator.NotEqual) {
        boolean evalValue = SQLEvalVisitorUtils.eq(leftValue, rightValue);
        x.putAttribute(EVAL_VALUE, !evalValue);
        return !evalValue;
      }
    }

    Object leftResult = getValue(visitor, x.getLeft());
    Object rightResult = getValue(visitor, x.getRight());

    if (x.getOperator() == SQLBinaryOperator.Like && leftResult instanceof String && leftResult
        .equals(rightResult)) {
      addViolation(visitor, ErrorCode.SAME_CONST_LIKE, "same const like", x);
    }

    if (x.getOperator() == SQLBinaryOperator.Like || x.getOperator() == SQLBinaryOperator.NotLike) {
      WallContext context = WallContext.current();
      if (context != null) {
        if (rightResult instanceof Number || leftResult instanceof Number) {
          context.incrementLikeNumberWarnings();
        }
      }
    }

    String dbType = null;
    WallContext wallContext = WallContext.current();
    if (wallContext != null) {
      dbType = wallContext.getDbType();
    }

    return eval(visitor, dbType, x, Collections.emptyList());
  }

  public static SQLExpr getFirst(SQLExpr x) {
    if (x instanceof SQLBinaryOpExpr) {
      SQLBinaryOpExpr binary = (SQLBinaryOpExpr) x;
      if (binary.getOperator() == SQLBinaryOperator.BooleanAnd
          || binary.getOperator() == SQLBinaryOperator.BooleanOr) {
        return getFirst(((SQLBinaryOpExpr) x).getLeft());
      }
    }

    return x;
  }

  public static List<SQLExpr> getParts(SQLExpr x) {
    List<SQLExpr> exprs = new ArrayList<SQLExpr>();
    exprs.add(x);

    while (true) {
      List<SQLExpr> tmp = partExpr(exprs);

      if (tmp.size() == exprs.size()) {
        break;
      }
      exprs = tmp;
    }

    return exprs;
  }

  public static List<SQLExpr> partExpr(List<SQLExpr> exprs) {
    List<SQLExpr> partList = new ArrayList<SQLExpr>();

    for (SQLExpr x : exprs) {
      if (x instanceof SQLBinaryOpExpr) {
        SQLBinaryOpExpr binary = (SQLBinaryOpExpr) x;
        if (binary.getOperator() == SQLBinaryOperator.BooleanAnd
            || binary.getOperator() == SQLBinaryOperator.BooleanOr) {
          partList.add(((SQLBinaryOpExpr) x).getLeft());
          partList.add(((SQLBinaryOpExpr) x).getRight());

          continue;
        }
      }
      partList.add(x);
    }
    return partList;
  }

  public static boolean isFirst(SQLObject x) {
    if (x == null) {
      return true;
    }

    for (; ; ) {
      SQLObject parent = x.getParent();
      if (!(parent instanceof SQLExpr)) {
        return true;
      }

      if (parent instanceof SQLBinaryOpExpr) {
        SQLBinaryOpExpr binaryExpr = (SQLBinaryOpExpr) parent;
        if (x == binaryExpr.getRight()) {
          return false;
        }
      }
      x = parent;
    }
  }

  private static boolean hasWhere(SQLSelectQuery selectQuery) {

    if (selectQuery instanceof SQLSelectQueryBlock) {
      return ((SQLSelectQueryBlock) selectQuery).getWhere() != null;
    } else if (selectQuery instanceof SQLUnionQuery) {
      SQLUnionQuery union = (SQLUnionQuery) selectQuery;
      return hasWhere(union.getLeft()) || hasWhere(union.getRight());
    }
    return false;
  }

  public static boolean checkSqlExpr(SQLExpr x) { // check groupby, orderby, limit
    if (x == null) {
      return false;
    }

    SQLObject obj = x;
    for (; ; ) {
      SQLObject parent = obj.getParent();

      if (parent == null) {
        return false;
      }

      if (parent instanceof SQLSelectGroupByClause) {
        return true;
      } else if (parent instanceof SQLOrderBy) {
        return true;
      } else if (parent instanceof SQLLimit) {
        return true;
      } else if (parent instanceof MySqlOrderingExpr) {
        return true;
      }

      obj = parent;
    }
  }

  public static boolean isWhereOrHaving(SQLObject x) {
    if (x == null) {
      return false;
    }

    for (; ; ) {
      SQLObject parent = x.getParent();

      if (parent == null) {
        return false;
      }

      if (parent instanceof SQLJoinTableSource) {
        SQLJoinTableSource joinTableSource = (SQLJoinTableSource) parent;
        if (joinTableSource.getCondition() == x) {
          return true;
        }

      }

      if (parent instanceof SQLUnionQuery) {
        SQLUnionQuery union = (SQLUnionQuery) parent;
        if (union.getRight() == x && hasWhere(union.getLeft())) {
          return true;
        }
      }

      if (parent instanceof SQLSelectQueryBlock) {
        SQLSelectQueryBlock query = (SQLSelectQueryBlock) parent;
        if (query.getWhere() == x) {
          return true;
        }
      }

      if (parent instanceof SQLDeleteStatement) {
        SQLDeleteStatement delete = (SQLDeleteStatement) parent;
        if (delete.getWhere() == x) {
          return true;
        } else {
          return false;
        }
      }

      if (parent instanceof SQLUpdateStatement) {
        SQLUpdateStatement update = (SQLUpdateStatement) parent;
        if (update.getWhere() == x) {
          return true;
        } else {
          return false;
        }
      }

      if (parent instanceof SQLSelectGroupByClause) {
        SQLSelectGroupByClause groupBy = (SQLSelectGroupByClause) parent;
        if (x == groupBy.getHaving()) {
          return true;
        } else {
          return false;
        }
      }

      x = parent;
    }
  }

  public static WallConditionContext getWallConditionContext() {
    return wallConditionContextLocal.get();
  }

  public static WallTopStatementContext getWallTopStatementContext() {
    return wallTopStatementContextLocal.get();
  }

  public static void clearWallTopStatementContext() {
    wallTopStatementContextLocal.set(null);
  }

  public static void initWallTopStatementContext() {
    wallTopStatementContextLocal.set(new WallTopStatementContext());
  }

  public static Object getConditionValue(WallVisitor visitor, SQLExpr x, boolean alwayTrueCheck) {
    final WallConditionContext old = wallConditionContextLocal.get();
    try {
      wallConditionContextLocal.set(new WallConditionContext());
      final Object value = getValue(visitor, x);

      final WallConditionContext current = wallConditionContextLocal.get();
      WallContext context = WallContext.current();
      if (context != null) {
        if (current.hasPartAlwayTrue() || Boolean.TRUE == value) {
          if (!isFirst(x)) {
            context.incrementWarnings();
          }
        }
      }

      if (current.hasPartAlwayTrue() && !visitor.getConfig().isConditionAndAlwayTrueAllow()) {
        addViolation(visitor, ErrorCode.ALWAYS_TRUE, "part alway true condition not allow", x);
      }

      if (current.hasPartAlwayFalse() && !visitor.getConfig().isConditionAndAlwayFalseAllow()) {
        addViolation(visitor, ErrorCode.ALWAYS_FALSE, "part alway false condition not allow", x);
      }

      if (current.hasConstArithmetic() && !visitor.getConfig().isConstArithmeticAllow()) {
        addViolation(visitor, ErrorCode.CONST_ARITHMETIC, "const arithmetic not allow", x);
      }

      if (current.hasXor() && !visitor.getConfig().isConditionOpXorAllow()) {
        addViolation(visitor, ErrorCode.XOR, "xor not allow", x);
      }

      if (current.hasBitwise() && !visitor.getConfig().isConditionOpBitwseAllow()) {
        addViolation(visitor, ErrorCode.BITWISE, "bitwise operator not allow", x);
      }

      return value;
    } finally {
      wallConditionContextLocal.set(old);
    }
  }

  public static Object getValueFromAttributes(WallVisitor visitor, SQLObject sqlObject) {
    if (sqlObject == null) {
      return null;
    }

    if (visitor != null && visitor.getConfig().isConditionLikeTrueAllow()
        && sqlObject.getAttributes().containsKey(HAS_TRUE_LIKE)) {
      return null;
    }
    return sqlObject.getAttribute(EVAL_VALUE);
  }

  public static Object getValue(SQLExpr x) {
    return getValue(null, x);
  }

  public static Object getValue(WallVisitor visitor, SQLExpr x) {
    if (x != null && x.getAttributes().containsKey(EVAL_VALUE)) {
      return getValueFromAttributes(visitor, x);
    }

    if (x instanceof SQLBinaryOpExpr) {
      return getValue(visitor, (SQLBinaryOpExpr) x);
    }

    if (x instanceof SQLBooleanExpr) {
      return ((SQLBooleanExpr) x).getValue();
    }

    if (x instanceof SQLNumericLiteralExpr) {
      return ((SQLNumericLiteralExpr) x).getNumber();
    }

    if (x instanceof SQLCharExpr) {
      return ((SQLCharExpr) x).getText();
    }

    if (x instanceof SQLNCharExpr) {
      return ((SQLNCharExpr) x).getText();
    }

    if (x instanceof SQLNotExpr) {
      Object result = getValue(visitor, ((SQLNotExpr) x).getExpr());
      if (result instanceof Boolean) {
        return !((Boolean) result).booleanValue();
      }
    }

    if (x instanceof SQLQueryExpr) {

      if (isSimpleCountTableSource(visitor, ((SQLQueryExpr) x).getSubQuery())) {
        return Integer.valueOf(1);
      }

      if (isSimpleCaseTableSource(visitor, ((SQLQueryExpr) x).getSubQuery())) {
        SQLSelectQueryBlock queryBlock = (SQLSelectQueryBlock) ((SQLQueryExpr) x).getSubQuery()
            .getQuery();
        SQLCaseExpr caseExpr = (SQLCaseExpr) queryBlock.getSelectList().get(0).getExpr();

        Object result = getValue(caseExpr);

        if (visitor != null && !visitor.getConfig().isCaseConditionConstAllow()) {
          boolean leftIsName = false;
          if (x.getParent() instanceof SQLBinaryOpExpr) {
            SQLExpr left = ((SQLBinaryOpExpr) x.getParent()).getLeft();
            if (left instanceof SQLName) {
              leftIsName = true;
            }
          }

          if (!leftIsName && result != null) {
            addViolation(visitor, ErrorCode.CONST_CASE_CONDITION, "const case condition", caseExpr);
          }
        }

        return result;
      }
    }

    String dbType = null;
    if (visitor != null) {
      dbType = visitor.getDbType();
    }

    if (x instanceof SQLMethodInvokeExpr //
        || x instanceof SQLBetweenExpr //
        || x instanceof SQLInListExpr //
        || x instanceof SQLUnaryExpr //
        ) {
      return eval(visitor, dbType, x, Collections.emptyList());
    }

    if (x instanceof SQLCaseExpr) {

      if (visitor != null && !visitor.getConfig().isCaseConditionConstAllow()) {
        SQLCaseExpr caseExpr = (SQLCaseExpr) x;

        boolean leftIsName = false;
        if (caseExpr.getParent() instanceof SQLBinaryOpExpr) {
          SQLExpr left = ((SQLBinaryOpExpr) caseExpr.getParent()).getLeft();
          if (left instanceof SQLName) {
            leftIsName = true;
          }
        }

        if (!leftIsName && caseExpr.getValueExpr() == null && caseExpr.getItems().size() > 0) {
          SQLCaseExpr.Item item = caseExpr.getItems().get(0);
          Object conditionVal = getValue(visitor, item.getConditionExpr());
          Object itemVal = getValue(visitor, item.getValueExpr());
          if (conditionVal instanceof Boolean && itemVal != null) {
            addViolation(visitor, ErrorCode.CONST_CASE_CONDITION, "const case condition", caseExpr);
          }
        }
      }

      return eval(visitor, dbType, x, Collections.emptyList());
    }

    return null;
  }

  public static Object eval(WallVisitor wallVisitor, String dbType, SQLObject sqlObject,
      List<Object> parameters) {
    SQLEvalVisitor visitor = SQLEvalVisitorUtils.createEvalVisitor(dbType);
    visitor.setParameters(parameters);
    visitor.registerFunction("rand", Nil.instance);
    visitor.registerFunction("sin", Nil.instance);
    visitor.registerFunction("cos", Nil.instance);
    visitor.registerFunction("asin", Nil.instance);
    visitor.registerFunction("acos", Nil.instance);
    sqlObject.accept(visitor);

    if (sqlObject instanceof SQLNumericLiteralExpr) {
      return ((SQLNumericLiteralExpr) sqlObject).getNumber();
    }
    return getValueFromAttributes(wallVisitor, sqlObject);
  }

  public static boolean isSimpleCountTableSource(WallVisitor visitor, SQLTableSource tableSource) {
    if (!(tableSource instanceof SQLSubqueryTableSource)) {
      return false;
    }

    SQLSubqueryTableSource subQuery = (SQLSubqueryTableSource) tableSource;

    return isSimpleCountTableSource(visitor, subQuery.getSelect());
  }

  public static boolean isSimpleCountTableSource(WallVisitor visitor, SQLSelect select) {
    SQLSelectQuery query = select.getQuery();

    if (query instanceof SQLSelectQueryBlock) {
      SQLSelectQueryBlock queryBlock = (SQLSelectQueryBlock) query;


      if (queryBlock.getWhere() != null) {
        Boolean whereValue = (Boolean) getValue(visitor, queryBlock.getWhere());
        if (!whereValue.booleanValue()) {
          return false;
        }
      }
      boolean simpleCount = false;
      if (queryBlock.getSelectList().size() == 1) {
        SQLExpr selectItemExpr = queryBlock.getSelectList().get(0).getExpr();
        if (selectItemExpr instanceof SQLAggregateExpr) {
          if (((SQLAggregateExpr) selectItemExpr).getMethodName().equalsIgnoreCase("COUNT")) {
            simpleCount = true;
          }
        }
      }

      if (simpleCount) {
        return true;
      }
    }

    return false;
  }

  public static boolean isSimpleCaseTableSource(WallVisitor visitor, SQLSelect select) {
    SQLSelectQuery query = select.getQuery();

    if (query instanceof SQLSelectQueryBlock) {
      SQLSelectQueryBlock queryBlock = (SQLSelectQueryBlock) query;

      if (queryBlock.getWhere() != null) {
        Boolean whereValue = (Boolean) getValue(visitor, queryBlock.getWhere());
        if (!whereValue) {
          return false;
        }
      }
      boolean simpleCase = false;
      if (queryBlock.getSelectList().size() == 1) {
        SQLExpr selectItemExpr = queryBlock.getSelectList().get(0).getExpr();
        if (selectItemExpr instanceof SQLCaseExpr) {
          simpleCase = true;
        }
      }

      if (simpleCase) {
        return true;
      }
    }

    return false;
  }

  public static void checkFunctionInTableSource(WallVisitor visitor, SQLMethodInvokeExpr x) {
    final WallTopStatementContext topStatementContext = wallTopStatementContextLocal.get();
    if (topStatementContext != null && (topStatementContext.fromSysSchema
        || topStatementContext.fromSysTable)) {
      return;
    }

    checkSchema(visitor, x.getOwner());

    String methodName = x.getMethodName().toLowerCase();
    if (!visitor.getProvider().checkDenyTable(methodName)) {
      if (isTopStatementWithTableSource(x) || isFirstSelectTableSource(x)) {
        if (topStatementContext != null) {
          topStatementContext.setFromSysSchema(Boolean.TRUE);
          clearViolation(visitor);
        }
      }
    }
  }

  public static void checkFunction(WallVisitor visitor, SQLMethodInvokeExpr x) {

    final WallTopStatementContext topStatementContext = wallTopStatementContextLocal.get();
    if (topStatementContext != null && (topStatementContext.fromSysSchema
        || topStatementContext.fromSysTable)) {
      return;
    }

    checkSchema(visitor, x.getOwner());

    if (!visitor.getConfig().isFunctionCheck()) {
      return;
    }

    String methodName = x.getMethodName().toLowerCase();

    WallContext context = WallContext.current();
    if (context != null) {
      context.incrementFunctionInvoke(methodName);
    }

    if (!visitor.getProvider().checkDenyFunction(methodName)) {
      boolean isTopNoneFrom = isTopNoneFromSelect(visitor, x);
      if (isTopNoneFrom) {
        return;
      }

      if (isTopFromDenySchema(visitor, x)) {
        return;
      }

      boolean isShow = x.getParent() instanceof MySqlShowGrantsStatement;
      if (isShow) {
        return;
      }

      if (isWhereOrHaving(x) || checkSqlExpr(x)) {
        addViolation(visitor, ErrorCode.FUNCTION_DENY, "deny function : " + methodName, x);
      }
    }
  }

  public static SQLSelectQueryBlock getQueryBlock(SQLObject x) {
    if (x == null) {
      return null;
    }

    if (x instanceof SQLSelectQueryBlock) {
      return (SQLSelectQueryBlock) x;
    }

    SQLObject parent = x.getParent();

    if (parent instanceof SQLExpr) {
      return getQueryBlock(parent);
    }

    if (parent instanceof SQLSelectItem) {
      return getQueryBlock(parent);
    }

    if (parent instanceof SQLSelectQueryBlock) {
      return (SQLSelectQueryBlock) parent;
    }

    return null;
  }

  public static boolean isTopNoneFromSelect(WallVisitor visitor, SQLObject x) {
    for (; ; ) {
      if ((x.getParent() instanceof SQLExpr) || (x.getParent() instanceof Item)) {
        x = x.getParent();
      } else {
        break;
      }
    }

    if (!(x.getParent() instanceof SQLSelectItem)) {
      return false;
    }

    SQLSelectItem item = (SQLSelectItem) x.getParent();

    if (!(item.getParent() instanceof SQLSelectQueryBlock)) {
      return false;
    }

    SQLSelectQueryBlock queryBlock = (SQLSelectQueryBlock) item.getParent();
    if (!queryBlockFromIsNull(visitor, queryBlock)) {
      return false;
    }

    if (!(queryBlock.getParent() instanceof SQLSelect)) {
      return false;
    }

    SQLSelect select = (SQLSelect) queryBlock.getParent();

    if (!(select.getParent() instanceof SQLSelectStatement)) {
      return false;
    }

    SQLSelectStatement stmt = (SQLSelectStatement) select.getParent();

    return stmt.getParent() == null;
  }

  private static boolean isTopFromDenySchema(WallVisitor visitor, SQLMethodInvokeExpr x) {
    SQLObject parent = x.getParent();
    for (; ; ) {
      if (parent instanceof SQLExpr || parent instanceof Item || parent instanceof SQLSelectItem) {
        parent = parent.getParent();
      } else {
        break;
      }
    }

    if (parent instanceof SQLSelectQueryBlock) {
      SQLSelectQueryBlock queryBlock = (SQLSelectQueryBlock) parent;
      if (!(queryBlock.getParent() instanceof SQLSelect)) {
        return false;
      }

      SQLSelect select = (SQLSelect) queryBlock.getParent();

      if (!(select.getParent() instanceof SQLSelectStatement)) {
        return false;
      }

      SQLSelectStatement stmt = (SQLSelectStatement) select.getParent();

      if (stmt.getParent() != null) {
        return false;
      }

      SQLTableSource from = queryBlock.getFrom();
      if (from instanceof SQLExprTableSource) {
        SQLExpr fromExpr = ((SQLExprTableSource) from).getExpr();
        if (fromExpr instanceof SQLName) {
          String fromTableName = fromExpr.toString();
          return visitor.isDenyTable(fromTableName);
        }
      }

      return false;
    }

    return false;
  }

  private static boolean checkSchema(WallVisitor visitor, SQLExpr x) {
    final WallTopStatementContext topStatementContext = wallTopStatementContextLocal.get();
    if (topStatementContext != null && (topStatementContext.fromSysSchema
        || topStatementContext.fromSysTable)) {
      return true;
    }

    if (x instanceof SQLName) {
      String owner = ((SQLName) x).getSimpleName();
      owner = WallVisitorUtils.form(owner);
      if (isInTableSource(x) && !visitor.getProvider().checkDenySchema(owner)) {

        if (!isTopStatementWithTableSource(x) && !isFirstSelectTableSource(x) && !isFirstInSubQuery(
            x)) {
          SQLObject parent = x.getParent();
          while (parent != null && !(parent instanceof SQLStatement)) {
            parent = parent.getParent();
          }

          boolean sameToTopSelectSchema = false;
          if (parent instanceof SQLSelectStatement) {
            SQLSelectStatement selectStmt = (SQLSelectStatement) parent;
            SQLSelectQuery query = selectStmt.getSelect().getQuery();
            if (query instanceof SQLSelectQueryBlock) {
              SQLSelectQueryBlock queryBlock = (SQLSelectQueryBlock) query;
              SQLTableSource from = queryBlock.getFrom();

              while (from instanceof SQLJoinTableSource) {
                from = ((SQLJoinTableSource) from).getLeft();
              }
              if (from instanceof SQLExprTableSource) {
                SQLExpr expr = ((SQLExprTableSource) from).getExpr();
                if (expr instanceof SQLPropertyExpr) {
                  SQLExpr schemaExpr = ((SQLPropertyExpr) expr).getOwner();
                  if (schemaExpr instanceof SQLIdentifierExpr) {
                    String schema = ((SQLIdentifierExpr) schemaExpr).getName();
                    schema = form(schema);
                    if (schema.equalsIgnoreCase(owner)) {
                      sameToTopSelectSchema = true;
                    }
                  }
                }
              }
            }
          }

          if (!sameToTopSelectSchema) {
            addViolation(visitor, ErrorCode.SCHEMA_DENY, "deny schema : " + owner, x);
          }
        } else {
          if (topStatementContext != null) {
            topStatementContext.setFromSysSchema(Boolean.TRUE);
            clearViolation(visitor);
          }
        }
        return true;
      }

      if (visitor.getConfig().isDenyObjects(owner)) {
        addViolation(visitor, ErrorCode.OBJECT_DENY, "deny object : " + owner, x);
        return true;
      }
    }

    // if (ownerExpr instanceof SQLPropertyExpr) {
    if (x instanceof SQLPropertyExpr) {
      return checkSchema(visitor, ((SQLPropertyExpr) x).getOwner());
    }

    return true;
  }

  private static boolean isInTableSource(SQLObject x) {

    for (; ; ) {
      if (x instanceof SQLExpr) {
        x = x.getParent();
      } else {
        break;
      }
    }

    if (x instanceof SQLExprTableSource) {
      return true;
    }
    return false;
  }

  private static boolean isFirstInSubQuery(SQLObject x) {
    for (; ; ) {
      if (x instanceof SQLExpr) {
        x = x.getParent();
      } else {
        break;
      }
    }

    if (!(x instanceof SQLExprTableSource)) {
      return false;
    }

    SQLSelect sqlSelect = null;
    SQLObject parent = x.getParent();
    while (parent != null) {

      if (parent instanceof SQLSelect) {
        sqlSelect = (SQLSelect) parent;
        break;
      }

      x = parent;
      parent = x.getParent();
    }

    if (sqlSelect == null) {
      return false;
    }

    parent = sqlSelect.getParent();
    if (!(parent instanceof SQLInSubQueryExpr && isFirst(parent))) {
      return false;
    }

    SQLInSubQueryExpr sqlInSubQueryExpr = (SQLInSubQueryExpr) parent;
    if (!(sqlInSubQueryExpr.getParent() instanceof SQLSelectQueryBlock)) {
      return false;
    }

    SQLSelectQueryBlock queryBlock = (SQLSelectQueryBlock) sqlInSubQueryExpr.getParent();
    if (!(queryBlock.getParent() instanceof SQLSelect)) {
      return false;
    }

    SQLSelect select = (SQLSelect) queryBlock.getParent();
    if (!(select.getParent() instanceof SQLSelectStatement)) {
      return false;
    }

    SQLSelectStatement stmt = (SQLSelectStatement) select.getParent();
    return stmt.getParent() == null;
  }

  private static boolean isFirstSelectTableSource(SQLObject x) {

    for (; ; ) {
      if (x instanceof SQLExpr) {
        x = x.getParent();
      } else {
        break;
      }
    }

    if (!(x instanceof SQLExprTableSource)) {
      return false;
    }

    SQLSelectQueryBlock queryBlock = null;
    SQLObject parent = x.getParent();
    while (parent != null) {

      // if (parent instanceof SQLJoinTableSource) {
      // SQLJoinTableSource join = (SQLJoinTableSource) parent;
      // if (join.getRight() == x && hasTableSource(join.getLeft())) {
      // return false;
      // }
      // }

      if (parent instanceof SQLSelectQueryBlock) {
        queryBlock = (SQLSelectQueryBlock) parent;
        break;
      }

      x = parent;
      parent = x.getParent();
    }

    if (queryBlock == null) {
      return false;
    }

    boolean isWhereQueryExpr = false;
    boolean isSelectItem = false;
    do {
      x = parent;
      parent = parent.getParent();
      if (parent instanceof SQLUnionQuery) {
        SQLUnionQuery union = (SQLUnionQuery) parent;
        if (union.getRight() == x && hasTableSource(union.getLeft())) {
          return false;
        }
      } else if (parent instanceof SQLQueryExpr || parent instanceof SQLInSubQueryExpr
          || parent instanceof SQLExistsExpr) {
        isWhereQueryExpr = isWhereOrHaving(parent);
      } else if (parent instanceof SQLSelectItem) {
        isSelectItem = true;
      } else if ((isWhereQueryExpr || isSelectItem) && parent instanceof SQLSelectQueryBlock) {
        if (hasTableSource((SQLSelectQueryBlock) parent)) {
          return false;
        }
      }

    } while (parent != null);

    return true;
  }

  private static boolean hasTableSource(SQLSelectQuery x) {

    if (x instanceof SQLUnionQuery) {
      SQLUnionQuery union = (SQLUnionQuery) x;
      return hasTableSource(union.getLeft()) || hasTableSource(union.getRight());
    } else if (x instanceof SQLSelectQueryBlock) {
      return hasTableSource(((SQLSelectQueryBlock) x).getFrom());
    }

    return false;
  }

  private static boolean hasTableSource(SQLTableSource x) {
    if (x == null) {
      return false;
    }

    if (x instanceof SQLExprTableSource) {
      SQLExpr fromExpr = ((SQLExprTableSource) x).getExpr();
      if (fromExpr instanceof SQLName) {
        String name = fromExpr.toString();
        name = form(name);
        if (name.equalsIgnoreCase("DUAL")) {
          return false;
        }
      }
      return true;
    } else if (x instanceof SQLJoinTableSource) {
      SQLJoinTableSource join = (SQLJoinTableSource) x;
      return hasTableSource(join.getLeft()) || hasTableSource(join.getRight());
    } else if (x instanceof SQLSubqueryTableSource) {
      return hasTableSource(((SQLSubqueryTableSource) x).getSelect().getQuery());
    }

    return false;
  }

  private static boolean isTopStatementWithTableSource(SQLObject x) {

    for (; ; ) {
      if (x instanceof SQLExpr) {
        x = x.getParent();
      } else {
        break;
      }
    }

    if (x instanceof SQLExprTableSource) {
      x = x.getParent();

      if (x instanceof SQLStatement) {
        x = x.getParent();
        if (x == null) {
          return true;
        }
      }
    }
    return false;
  }

  private static boolean isTopSelectItem(SQLObject x) {
    for (; ; ) {
      if ((x.getParent() instanceof SQLExpr) || (x.getParent() instanceof Item)) {
        x = x.getParent();
      } else {
        break;
      }
    }

    if (!(x.getParent() instanceof SQLSelectItem)) {
      return false;
    }

    SQLSelectItem item = (SQLSelectItem) x.getParent();
    return isTopSelectStatement(item.getParent());
  }

  private static boolean isTopSelectStatement(SQLObject x) {

    if (!(x instanceof SQLSelectQueryBlock)) {
      return false;
    }

    SQLSelectQueryBlock queryBlock = (SQLSelectQueryBlock) x;
    if (!(queryBlock.getParent() instanceof SQLSelect)) {
      return false;
    }

    SQLSelect select = (SQLSelect) queryBlock.getParent();
    if (!(select.getParent() instanceof SQLSelectStatement)) {
      return false;
    }

    SQLSelectStatement stmt = (SQLSelectStatement) select.getParent();
    return stmt.getParent() == null;
  }

  public static boolean isTopSelectOutFile(MySqlOutFileExpr x) {
    if (!(x.getParent() instanceof SQLExprTableSource)) {
      return false;
    }
    SQLExprTableSource tableSource = (SQLExprTableSource) x.getParent();
    return isTopSelectStatement(tableSource.getParent());
  }

  public static boolean check(WallVisitor visitor, SQLExprTableSource x) {
    final WallTopStatementContext topStatementContext = wallTopStatementContextLocal.get();

    SQLExpr expr = x.getExpr();
    if (expr instanceof SQLPropertyExpr) {
      boolean checkResult = checkSchema(visitor, ((SQLPropertyExpr) expr).getOwner());
      if (!checkResult) {
        return false;
      }
    }

    if (expr instanceof SQLName) {
      String tableName = ((SQLName) expr).getSimpleName();

      WallContext context = WallContext.current();
      if (context != null) {
        WallSqlTableStat tableStat = context.getTableStat(tableName);
        if (tableStat != null) {
          SQLObject parent = x.getParent();

          while (parent instanceof SQLTableSource) {
            parent = parent.getParent();
          }

          if (parent instanceof SQLSelectQueryBlock) {
            SQLSelectQueryBlock queryBlock = (SQLSelectQueryBlock) parent;
            if (x == queryBlock.getInto()) {
              tableStat.incrementSelectIntoCount();
            } else {
              tableStat.incrementSelectCount();
            }
          } else if (parent instanceof SQLTruncateStatement) {
            tableStat.incrementTruncateCount();
          } else if (parent instanceof SQLInsertStatement) {
            tableStat.incrementInsertCount();
          } else if (parent instanceof SQLDeleteStatement) {
            tableStat.incrementDeleteCount();
          } else if (parent instanceof SQLUpdateStatement) {
            tableStat.incrementUpdateCount();
          } else if (parent instanceof MySqlReplaceStatement) {
            tableStat.incrementReplaceCount();
          }
        }
      }

      if (topStatementContext != null && (topStatementContext.fromSysSchema
          || topStatementContext.fromSysTable)) {
        return true;
      }

      if (visitor.isDenyTable(tableName)
          && !(topStatementContext != null && topStatementContext.fromPermitTable())) {

        if (isTopStatementWithTableSource(x) || isFirstSelectTableSource(x)) {
          if (topStatementContext != null) {
            topStatementContext.setFromSysTable(Boolean.TRUE);
            clearViolation(visitor);
          }
          return false;
        }

        boolean isTopNoneFrom = isTopNoneFromSelect(visitor, x);
        if (isTopNoneFrom) {
          return false;
        }

        addViolation(visitor, ErrorCode.TABLE_DENY, "deny table : " + tableName, x);
        return false;
      }

      if (visitor.getConfig().getPermitTables().contains(tableName)) {
        if (isFirstSelectTableSource(x)) {
          if (topStatementContext != null) {
            topStatementContext.setFromPermitTable(Boolean.TRUE);
          }
          return false;
        }
      }
    }

    return true;
  }

  private static void addViolation(WallVisitor visitor, int errorCode, String message,
      SQLObject x) {
    visitor.addViolation(new IllegalSQLObjectViolation(errorCode, message, visitor.toSQL(x)));
  }

  private static void clearViolation(WallVisitor visitor) {
    visitor.getViolations().clear();
  }

  public static void checkUnion(WallVisitor visitor, SQLUnionQuery x) {

    if (x.getOperator() == SQLUnionOperator.MINUS && !visitor.getConfig().isMinusAllow()) {
      addViolation(visitor, ErrorCode.INTERSET_NOT_ALLOW, "minus not allow", x);
      return;
    }

    if (x.getOperator() == SQLUnionOperator.INTERSECT && !visitor.getConfig().isIntersectAllow()) {
      addViolation(visitor, ErrorCode.INTERSET_NOT_ALLOW, "intersect not allow", x);
      return;
    }

    if (!WallVisitorUtils.queryBlockFromIsNull(visitor, x.getLeft())
        && WallVisitorUtils.queryBlockFromIsNull(visitor, x.getRight())) {
      boolean isTopUpdateStatement = false;
      boolean isTopInsertStatement = false;
      SQLObject selectParent = x.getParent();
      while (selectParent instanceof SQLSelectQuery //
          || selectParent instanceof SQLJoinTableSource //
          || selectParent instanceof SQLSubqueryTableSource //
          || selectParent instanceof SQLSelect) {
        selectParent = selectParent.getParent();
      }

      if (selectParent instanceof SQLUpdateStatement) {
        isTopUpdateStatement = true;
      }

      if (selectParent instanceof SQLInsertStatement) {
        isTopInsertStatement = true;
      }

      if (isTopUpdateStatement || isTopInsertStatement) {
        return;
      }

      if (x.getLeft() instanceof SQLSelectQueryBlock) {
        SQLSelectQueryBlock left = (SQLSelectQueryBlock) x.getLeft();
        SQLTableSource tableSource = left.getFrom();
        if (left.getWhere() == null && tableSource != null
            && tableSource instanceof SQLExprTableSource) {
          return;
        }
      }

      WallContext context = WallContext.current();
      if (context != null) {
        context.incrementUnionWarnings();
      }

      if (((x.getOperator() == SQLUnionOperator.UNION
          || x.getOperator() == SQLUnionOperator.UNION_ALL
          || x.getOperator() == SQLUnionOperator.DISTINCT)
          && visitor.getConfig().isSelectUnionCheck() && visitor.isSqlEndOfComment())
          || (x.getOperator() == SQLUnionOperator.MINUS && visitor.getConfig().isSelectMinusCheck())
          || (x.getOperator() == SQLUnionOperator.INTERSECT && visitor.getConfig()
          .isSelectIntersectCheck())
          || (x.getOperator() == SQLUnionOperator.EXCEPT && visitor.getConfig()
          .isSelectExceptCheck())) {
        addViolation(visitor, ErrorCode.UNION,
            x.getOperator().toString() + " query not contains 'from clause'", x);
      }
    }
  }

  public static boolean queryBlockFromIsNull(WallVisitor visitor, SQLSelectQuery query) {
    return queryBlockFromIsNull(visitor, query, true);
  }

  public static boolean queryBlockFromIsNull(WallVisitor visitor, SQLSelectQuery query,
      boolean checkSelectConst) {
    if (query instanceof SQLSelectQueryBlock) {
      SQLSelectQueryBlock queryBlock = (SQLSelectQueryBlock) query;
      SQLTableSource from = queryBlock.getFrom();

      if (queryBlock.getSelectList().size() < 1) {
        return false;
      }

      if (from == null) {
        boolean itemIsConst = true;
        boolean itemHasAlias = false;
        for (SQLSelectItem item : queryBlock.getSelectList()) {
          if (item.getExpr() instanceof SQLIdentifierExpr || item
              .getExpr() instanceof SQLPropertyExpr) {
            itemIsConst = false;
            break;
          }
          if (item.getAlias() != null) {
            itemHasAlias = true;
            break;
          }
        }
        if (itemIsConst && !itemHasAlias) {
          return true;
        } else {
          return false;
        }
      }

      if (from instanceof SQLExprTableSource) {
        SQLExpr fromExpr = ((SQLExprTableSource) from).getExpr();
        if (fromExpr instanceof SQLName) {
          String name = fromExpr.toString();

          name = form(name);

          if (name.equalsIgnoreCase("DUAL")) {
            return true;
          }
        }
      }

      if (queryBlock.getSelectList().size() == 1
          && queryBlock.getSelectList().get(0).getExpr() instanceof SQLAllColumnExpr) {
        if (from instanceof SQLSubqueryTableSource) {
          SQLSelectQuery subQuery = ((SQLSubqueryTableSource) from).getSelect().getQuery();
          if (queryBlockFromIsNull(visitor, subQuery)) {
            return true;
          }
        }
      }

      if (checkSelectConst) {
        SQLExpr where = queryBlock.getWhere();
        if (where != null) {
          Object whereValue = getValue(visitor, where);
          if (Boolean.TRUE == whereValue) {
            boolean allIsConst = true;
            for (SQLSelectItem item : queryBlock.getSelectList()) {
              if (getValue(visitor, item.getExpr()) == null) {
                allIsConst = false;
                break;
              }
            }
            if (allIsConst) {
              return true;
            }
          }
        }
      }
    }

    return false;
  }

  public static String form(String name) {
    if (name.startsWith("\"") && name.endsWith("\"")) {
      name = name.substring(1, name.length() - 1);
    }

    if (name.startsWith("'") && name.endsWith("'")) {
      name = name.substring(1, name.length() - 1);
    }

    if (name.startsWith("`") && name.endsWith("`")) {
      name = name.substring(1, name.length() - 1);
    }

    name = name.toLowerCase();
    return name;
  }

  public static void loadResource(Set<String> names, String resource) {
    try {
      boolean hasResource = false;
      ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
      if (classLoader != null) {
        Enumeration<URL> e = Thread.currentThread().getContextClassLoader().getResources(resource);
        while (e.hasMoreElements()) {
          URL url = e.nextElement();
          InputStream in = null;
          try {
            in = url.openStream();
            readFromInputStream(names, in);

            hasResource = true;
          } finally {
            JdbcUtils.close(in);
          }
        }
      }

      // for aliyun odps
      if (!hasResource) {
        if (!resource.startsWith("/")) {
          resource = "/" + resource;
        }

        InputStream in = null;
        try {
          in = WallVisitorUtils.class.getResourceAsStream(resource);
          if (in != null) {
            readFromInputStream(names, in);
          }
        } finally {
          JdbcUtils.close(in);
        }
      }
    } catch (IOException e) {
      LOG.error("load oracle deny tables errror", e);
    }
  }

  private static void readFromInputStream(Set<String> names, InputStream in) throws IOException {
    BufferedReader reader = null;
    try {
      reader = new BufferedReader(new InputStreamReader(in));
      for (; ; ) {
        String line = reader.readLine();
        if (line == null) {
          break;
        }
        line = line.trim();
        if (line.length() > 0) {
          line = line.toLowerCase();
          names.add(line);
        }
      }
    } finally {
      JdbcUtils.close(reader);
    }
  }

  public static void preVisitCheck(WallVisitor visitor, SQLObject x) {
    WallConfig config = visitor.getProvider().getConfig();

    if (!(x instanceof SQLStatement)) {
      return;
    }

    boolean allow = false;
    int errorCode;
    String denyMessage;
    if (x instanceof SQLInsertStatement) {
      allow = config.isInsertAllow();
      denyMessage = "insert not allow";
      errorCode = ErrorCode.INSERT_NOT_ALLOW;
    } else if (x instanceof SQLSelectStatement) {
      allow = true;
      denyMessage = "select not allow";
      errorCode = ErrorCode.SELECT_NOT_ALLOW;
    } else if (x instanceof SQLDeleteStatement) {
      allow = config.isDeleteAllow();
      denyMessage = "delete not allow";
      errorCode = ErrorCode.DELETE_NOT_ALLOW;
    } else if (x instanceof SQLUpdateStatement) {
      allow = config.isUpdateAllow();
      denyMessage = "update not allow";
      errorCode = ErrorCode.UPDATE_NOT_ALLOW;
    } else if (x instanceof OracleMultiInsertStatement) {
      allow = true;
      denyMessage = "multi-insert not allow";
      errorCode = ErrorCode.INSERT_NOT_ALLOW;
    } else if (x instanceof SQLMergeStatement) {
      allow = config.isMergeAllow();
      denyMessage = "merge not allow";
      errorCode = ErrorCode.MERGE_NOT_ALLOW;
    } else if (x instanceof SQLCallStatement || x instanceof SQLServerExecStatement) {
      allow = config.isCallAllow();
      denyMessage = "call not allow";
      errorCode = ErrorCode.CALL_NOT_ALLOW;
    } else if (x instanceof SQLTruncateStatement) {
      allow = config.isTruncateAllow();
      denyMessage = "truncate not allow";
      errorCode = ErrorCode.TRUNCATE_NOT_ALLOW;
    } else if (x instanceof SQLCreateTableStatement //
        || x instanceof SQLCreateIndexStatement //
        || x instanceof SQLCreateViewStatement //
        || x instanceof SQLCreateTriggerStatement //
        || x instanceof SQLCreateSequenceStatement //
        ) {
      allow = config.isCreateTableAllow();
      denyMessage = "create table not allow";
      errorCode = ErrorCode.CREATE_TABLE_NOT_ALLOW;
    } else if (x instanceof SQLAlterTableStatement) {
      allow = config.isAlterTableAllow();
      denyMessage = "alter table not allow";
      errorCode = ErrorCode.ALTER_TABLE_NOT_ALLOW;
    } else if (x instanceof SQLDropTableStatement //
        || x instanceof SQLDropIndexStatement //
        || x instanceof SQLDropViewStatement //
        || x instanceof SQLDropTriggerStatement //
        || x instanceof SQLDropSequenceStatement //
        || x instanceof SQLDropProcedureStatement //
        ) {
      allow = config.isDropTableAllow();
      denyMessage = "drop table not allow";
      errorCode = ErrorCode.DROP_TABLE_NOT_ALLOW;
    } else if (x instanceof MySqlSetCharSetStatement //
        || x instanceof MySqlSetNamesStatement //
        || x instanceof SQLSetStatement //
        || x instanceof SQLServerSetStatement) {
      allow = config.isSetAllow();
      denyMessage = "set not allow";
      errorCode = ErrorCode.SET_NOT_ALLOW;
    } else if (x instanceof MySqlReplaceStatement) {
      allow = config.isReplaceAllow();
      denyMessage = "replace not allow";
      errorCode = ErrorCode.REPLACE_NOT_ALLOW;
    } else if (x instanceof SQLDescribeStatement
        || (x instanceof MySqlExplainStatement && ((MySqlExplainStatement) x).isDescribe())) {
      allow = config.isDescribeAllow();
      denyMessage = "describe not allow";
      errorCode = ErrorCode.DESC_NOT_ALLOW;
    } else if (x instanceof MySqlShowStatement
        || x instanceof PGShowStatement
        || x instanceof SQLShowTablesStatement) {
      allow = config.isShowAllow();
      denyMessage = "show not allow";
      errorCode = ErrorCode.SHOW_NOT_ALLOW;
    } else if (x instanceof MySqlCommitStatement || x instanceof SQLServerCommitStatement) {
      allow = config.isCommitAllow();
      denyMessage = "commit not allow";
      errorCode = ErrorCode.COMMIT_NOT_ALLOW;
    } else if (x instanceof SQLRollbackStatement) {
      allow = config.isRollbackAllow();
      denyMessage = "rollback not allow";
      errorCode = ErrorCode.ROLLBACK_NOT_ALLOW;
    } else if (x instanceof SQLUseStatement) {
      allow = config.isUseAllow();
      denyMessage = "use not allow";
      errorCode = ErrorCode.USE_NOT_ALLOW;
    } else if (x instanceof MySqlRenameTableStatement) {
      allow = config.isRenameTableAllow();
      denyMessage = "rename table not allow";
      errorCode = ErrorCode.RENAME_TABLE_NOT_ALLOW;
    } else if (x instanceof MySqlHintStatement) {
      allow = config.isHintAllow();
      denyMessage = "hint not allow";
      errorCode = ErrorCode.HINT_NOT_ALLOW;
    } else if (x instanceof MySqlLockTableStatement) {
      allow = config.isLockTableAllow();
      denyMessage = "lock table not allow";
      errorCode = ErrorCode.LOCK_TABLE_NOT_ALLOW;
    } else if (x instanceof SQLStartTransactionStatement) {
      allow = config.isStartTransactionAllow();
      denyMessage = "start transaction not allow";
      errorCode = ErrorCode.START_TRANSACTION_NOT_ALLOW;
    } else if (x instanceof SQLBlockStatement) {
      allow = config.isBlockAllow();
      denyMessage = "block statement not allow";
      errorCode = ErrorCode.BLOCK_NOT_ALLOW;
    } else {
      allow = config.isNoneBaseStatementAllow();
      errorCode = ErrorCode.NONE_BASE_STATEMENT_NOT_ALLOW;
      denyMessage = x.getClass() + " not allow";
    }

    if (!allow) {
      addViolation(visitor, errorCode, denyMessage, x);
    }
  }

  public static void check(WallVisitor visitor, SQLCommentHint x) {
    if (!visitor.getConfig().isHintAllow()) {
      addViolation(visitor, ErrorCode.EVIL_HINTS, "hint not allow", x);
      return;
    }

    String text = x.getText();
    text = text.trim();
    if (text.startsWith("!")) {
      text = text.substring(1);
    }

    if (text.length() == 0) {
      return;
    }

    int pos = 0;
    for (; pos < text.length(); pos++) {
      char ch = text.charAt(pos);
      if (ch >= '0' && ch <= '9') {
        continue;
      } else {
        break;
      }
    }

    if (pos == 5) {
      text = text.substring(5);
      text = text.trim();
    }

    text = text.toUpperCase();

    boolean isWhite = false;
    for (String hint : whiteHints) {
      if (text.equals(hint)) {
        isWhite = true;
        break;
      }
    }

    if (!isWhite) {
      if (text.startsWith("FORCE INDEX") || text.startsWith("IGNORE INDEX")) {
        isWhite = true;
      }
    }

    if (!isWhite) {
      if (text.startsWith("SET")) {
        SQLStatementParser parser = new MySqlStatementParser(text);
        List<SQLStatement> statementList = parser.parseStatementList();
        if (statementList != null && statementList.size() > 0) {
          SQLStatement statement = statementList.get(0);
          if (statement instanceof SQLSetStatement || statement instanceof MySqlSetCharSetStatement
              || statement instanceof MySqlSetNamesStatement) {
            isWhite = true;
          }
        }
      }
    }

    if (!isWhite) {
      addViolation(visitor, ErrorCode.EVIL_HINTS, "hint not allow", x);
    }
  }

  public static class WallTopStatementContext {

    private boolean fromSysTable = false;
    private boolean fromSysSchema = false;

    private boolean fromPermitTable = false;

    public boolean fromSysTable() {
      return fromSysTable;
    }

    public void setFromSysTable(boolean fromSysTable) {
      this.fromSysTable = fromSysTable;
    }

    public boolean fromSysSchema() {
      return fromSysSchema;
    }

    public void setFromSysSchema(boolean fromSysSchema) {
      this.fromSysSchema = fromSysSchema;
    }

    public boolean fromPermitTable() {
      return fromPermitTable;
    }

    public void setFromPermitTable(boolean fromPermitTable) {
      this.fromPermitTable = fromPermitTable;
    }
  }

  public static class WallConditionContext {

    private boolean partAlwayTrue = false;
    private boolean partAlwayFalse = false;
    private boolean constArithmetic = false;
    private boolean xor = false;
    private boolean bitwise = false;

    public boolean hasPartAlwayTrue() {
      return partAlwayTrue;
    }

    public void setPartAlwayTrue(boolean partAllowTrue) {
      this.partAlwayTrue = partAllowTrue;
    }

    public boolean hasPartAlwayFalse() {
      return partAlwayFalse;
    }

    public void setPartAlwayFalse(boolean partAlwayFalse) {
      this.partAlwayFalse = partAlwayFalse;
    }

    public boolean hasConstArithmetic() {
      return constArithmetic;
    }

    public void setConstArithmetic(boolean constArithmetic) {
      this.constArithmetic = constArithmetic;
    }

    public boolean hasXor() {
      return xor;
    }

    public void setXor(boolean xor) {
      this.xor = xor;
    }

    public boolean hasBitwise() {
      return bitwise;
    }

    public void setBitwise(boolean bitwise) {
      this.bitwise = bitwise;
    }

  }
}
